package org.wyona.yarep.impl.repo.jcr; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; import java.util.Date; import org.wyona.yarep.core.Node; import org.wyona.yarep.core.NoSuchNodeException; import org.wyona.yarep.core.NoSuchRevisionException; import org.wyona.yarep.core.NodeStateException; import org.wyona.yarep.core.NodeType; import org.wyona.yarep.core.Property; import org.wyona.yarep.core.PropertyType; import org.wyona.yarep.core.RepositoryException; import org.wyona.yarep.core.Revision; import org.wyona.yarep.impl.AbstractNode; import org.wyona.yarep.impl.DefaultProperty; import org.apache.log4j.Category; /** * This class represents a repository node. * A repository node may be either a collection ("directory") or a resource ("file"). * If it is a resource, it has an associated data content, which may be accessed by using * getInputStream()/getOutputStream() or getReader()/getWriter(). * To store textual data, the reader/writer methods should be used instead of the stream * methods to allow the implementation to handle textual data differently from binary data. * * @see org.wyona.yarep.core.Repository */ public class JCRNode implements Node { private static Category log = Category.getInstance(JCRNode.class); private javax.jcr.Node jcrNode; private javax.jcr.Session session; public static String BINARY_CONTENT_PROP_NAME = "binary-content"; /** * */ public JCRNode(javax.jcr.Node node, javax.jcr.Session session) { this.jcrNode = node; this.session = session; } /** * Gets the name of this node, which is the last part of the path. * @return name * @throws RepositoryException repository error */ public String getName() throws RepositoryException { try { return jcrNode.getName(); } catch (javax.jcr.RepositoryException e) { throw new RepositoryException(e.getMessage(), e); } } /** * Gets the parent node of this node. * @return parent node or null if this is the root node * @throws RepositoryException repository error */ public Node getParent() throws RepositoryException { log.error("Not implemented yet!"); return null; } /** * Deletes this node and all subnodes. * The root node cannot be deleted. * @throws RepositoryException if this node is the root node or if a repository error occurs. */ public void delete() throws RepositoryException { log.error("Not implemented yet!"); } /** * Gets the complete repository path of this node. * @return path * @throws RepositoryException repository error */ public String getPath() throws RepositoryException { try { return jcrNode.getPath(); } catch (javax.jcr.RepositoryException e) { throw new RepositoryException(e.getMessage(), e); } } /** * Gets the UUID of this node. * @return uuid * @throws RepositoryException repository error */ public String getUUID() throws RepositoryException { try { return jcrNode.getUUID(); } catch (javax.jcr.RepositoryException e) { throw new RepositoryException(e.getMessage(), e); } } /** * Gets the type of this node (collection or resource). * @return type * @throws RepositoryException repository error * @see org.wyona.yarep.core.Node#getType() */ public int getType() throws RepositoryException { log.error("Not implemented yet!"); return -1; //jcrNode.getDefinition().getDeclaringNodeType() } /** * Indicates whether this node is of type "resource". * @return true if type is resource * @throws RepositoryException repository error * @see org.wyona.yarep.core.Node#isResource() */ public boolean isResource() throws RepositoryException { try { if (jcrNode.hasNode("jcr:content") && jcrNode.getNode("jcr:content").hasProperty("jcr:data")) { //if (jcrNode.hasProperty(BINARY_CONTENT_PROP_NAME)) { return true; } else { log.warn("Node '" + jcrNode.getPath() + "' does not seem to be of type RESOURCE"); return false; } } catch (Exception e) { throw new RepositoryException(e.getMessage(), e); } } /** * Indicates whether this node is of type "collection". * @return true if type is collection * @throws RepositoryException repository error * @see org.wyona.yarep.core.Node#isCollection() */ public boolean isCollection() throws RepositoryException { log.error("Not implemented yet!"); return true; //return getType() == NodeType.COLLECTION; } /** * Indicates whether the content of this node is binary or textual. * Useful only if this node is a resource. * @return true if the content of this node is binary * @throws RepositoryException repository error */ //public boolean isBinary() throws RepositoryException; /** * Creates a new node and adds it as a direct child to this node. * @param name name of the child node * @param type node type of the child node * @return the new child node * @throws RepositoryException if this node is not a collection or if a repository error occurs */ public Node addNode(String name, int type) throws RepositoryException { try { javax.jcr.Node newNode = this.jcrNode.addNode(name, "nt:unstructured"); //javax.jcr.Node newNode = this.jcrNode.addNode(name, type == NodeType.COLLECTION ? "nt:folder" : "nt:file"); if (type != NodeType.COLLECTION) { javax.jcr.Node cNode = newNode.addNode("jcr:content", "nt:resource"); cNode.setProperty("jcr:data", ""); cNode.setProperty("jcr:mimeType", "application/xml"); cNode.setProperty("jcr:lastModified", java.util.Calendar.getInstance()); } this.session.save(); return new JCRNode(newNode, session); } catch (Exception e) { throw new RepositoryException(e.getMessage(), e); } } /** * Gets the child node with the given name. Must be a direct child. * @param name name of the child node * @return child node * @throws NoSuchNodeException if no child node with this name exists. * @throws RepositoryException if node is not a collection or if a repository error occurs */ public Node getNode(String name) throws NoSuchNodeException, RepositoryException { log.error("Not implemented yet!"); return null; } /** * Gets all child nodes. * @return child nodes or empty array if there are no child nodes. * @throws RepositoryException if node is not a collection or if a repository error occurs */ public Node[] getNodes() throws RepositoryException { log.error("Not implemented yet!"); return null; } /** * Indicates whether this node has a direct child node with the given name. * @param name * @return true if child node exists with the given id, false otherwise * @throws RepositoryException if node is not a collection or if a repository error occurs */ public boolean hasNode(String name) throws RepositoryException { try { return jcrNode.hasNode(name); } catch (Exception e) { throw new RepositoryException(e); } } /** * Gets the property with the given name. * @param name Property name * @return property or null if the property does not exist * @throws RepositoryException repository error */ public Property getProperty(String name) throws RepositoryException { try { javax.jcr.Property jcrProp = this.jcrNode.getProperty(name); int type = jcrProp.getType(); //int type = jcrProp.getDefinition().getRequiredType(); Property p = null; if (type == javax.jcr.PropertyType.STRING) { p = new DefaultProperty(name, PropertyType.STRING, this); p.setValue(this.jcrNode.getProperty(name).getValue().getString()); } else if (type == javax.jcr.PropertyType.BOOLEAN) { p = new DefaultProperty(name, PropertyType.BOOLEAN, this); p.setValue(this.jcrNode.getProperty(name).getValue().getBoolean()); } else if (type == javax.jcr.PropertyType.UNDEFINED) { log.warn("PropertyType of property '" + name + "' is UNDEFINED. Trying to convert to String ..."); p = new DefaultProperty(name, PropertyType.STRING, this); p.setValue(this.jcrNode.getProperty(name).getValue().getString()); } else { log.error("PropertyType not implemented yet: " + type); log.error("javax.jcr.PropertyType.UNDEFINED: " + javax.jcr.PropertyType.UNDEFINED); log.error("javax.jcr.PropertyType.STRING: " + javax.jcr.PropertyType.STRING); } return p; } catch (Exception e) { throw new RepositoryException(e.getMessage(), e); } } /** * Get all properties of this node * @return array of properties of this node or empty array if there are no properties. * @throws RepositoryException other error */ public Property[] getProperties() throws RepositoryException { log.error("Not implemented yet!"); return null; } /** * Indicates whether this node has a property with the given name. * @param name * @return true if a property exists with the given name, false otherwise * @throws RepositoryException repository error */ public boolean hasProperty(String name) throws RepositoryException { try { return this.jcrNode.hasProperty(name); } catch(javax.jcr.RepositoryException e) { throw new RepositoryException(e.getMessage(), e); } } //public boolean hasProperties() throws RepositoryException; /** * Removes the property with the given name. * Does nothing if no property with the given name exists. * @param name * @throws RepositoryException repository error */ public void removeProperty(String name) throws RepositoryException { log.error("Not implemented yet!"); } /** * Sets a property of type boolean or creates it if it does not exist yet. * @param name * @param value * @return the set property * @throws RepositoryException repository error */ public Property setProperty(String name, boolean value) throws RepositoryException { try { this.jcrNode.setProperty(name, value); session.save(); Property p = new DefaultProperty(name, PropertyType.STRING, this); p.setValue(value); return p; } catch (Exception e) { throw new RepositoryException(e.getMessage(), e); } } /** * Sets a property of type date or creates it if it does not exist yet. * @param name * @param value * @return the set property * @throws RepositoryException repository error */ public Property setProperty(String name, Date value) throws RepositoryException { try { java.util.Calendar cal = new java.util.GregorianCalendar(); cal.setTime(value); this.jcrNode.setProperty(name, cal); session.save(); Property p = new DefaultProperty(name, PropertyType.STRING, this); p.setValue(value); return p; } catch (Exception e) { throw new RepositoryException(e.getMessage(), e); } } /** * Sets a property of type double or creates it if it does not exist yet. * @param name * @param value * @return the set property * @throws RepositoryException repository error */ public Property setProperty(String name, double value) throws RepositoryException { log.error("Not implemented yet!"); return null; } //public Property setProperty(String name, InputStream value) throws RepositoryException; /** * Sets a property of type long or creates it if it does not exist yet. * @param name * @param value * @return the set property * @throws RepositoryException repository error */ public Property setProperty(String name, long value) throws RepositoryException { log.error("Not implemented yet!"); return null; } /** * Sets a property of type string or creates it if it does not exist yet. * @param name * @param value * @return the set property * @throws RepositoryException repository error */ public Property setProperty(String name, String value) throws RepositoryException { try { log.warn("Property: " + name + ", " + value); this.jcrNode.setProperty(name, value); //this.jcrNode.setProperty(name, value, javax.jcr.PropertyType.STRING); session.save(); Property p = new DefaultProperty(name, PropertyType.STRING, this); p.setValue(value); return p; } catch (Exception e) { throw new RepositoryException(e.getMessage(), e); } } /** * Sets a property or creates it if it does not exist yet. * @param property * @throws RepositoryException repository error */ public void setProperty(Property property) throws RepositoryException { try { if (property.getType() == PropertyType.STRING) { //if () { this.jcrNode.setProperty(property.getName(), property.getString()); //} session.save(); } else { log.error("Not implemented yet!"); } } catch (Exception e) { throw new RepositoryException(e.getMessage(), e); } } //public Property getDefaultProperty() throws RepositoryException; /** * Gets an input stream of the binary data content of this node. * Useful only for nodes of type resource. * @return input stream * @throws RepositoryException repository error */ public InputStream getInputStream() throws RepositoryException { try { //return jcrNode.getProperty(BINARY_CONTENT_PROP_NAME).getStream(); return jcrNode.getNode("jcr:content").getProperty("jcr:data").getStream(); } catch (Exception e) { throw new RepositoryException(e.getMessage(), e); } } //public void setInputStream(InputStream inputStream) throws RepositoryException; /** * Gets an output stream of the binary data content of this node. * Useful only for nodes of type resource. * Don't forget to close the stream because some implementations may * require that. * @return output stream * @throws RepositoryException repository error */ public OutputStream getOutputStream() throws RepositoryException { return new JCROutputStream(this); } /** * Gets a reader of the data content of this node. Use this method if the * node contains character data. * Useful only for nodes of type resource. * @return reader * @throws RepositoryException repository error */ //public Reader getReader() throws RepositoryException; /** * Gets a writer of the data content of this node. Use this method if the * node contains character data. * Useful only for nodes of type resource. * Don't forget to close the writer because some implementations may * require that. * @return writer * @throws RepositoryException repository error */ //public Writer getWriter() throws RepositoryException; /** * Puts this node into checked-in state and creates a new revision. * @return the new revision * @throws NodeStateException if node is not in checked out state * @throws RepositoryException repository error */ public Revision checkin() throws NodeStateException, RepositoryException { return checkin(""); } /** * Puts this node into checked-in state and creates a new revision. * @param comment a comment to add to the new revision. * @return the new revision * @throws NodeStateException if node is not in checked out state * @throws RepositoryException repository error */ public Revision checkin(String comment) throws NodeStateException, RepositoryException { if (!isCheckedOut()) { try { throw new NodeStateException("Node " + jcrNode.getPath() + " is not checked out."); } catch(javax.jcr.RepositoryException e) { log.error(e, e); throw new RepositoryException(e.getMessage(), e); } } //Revision revision = createRevision(comment); setProperty(org.wyona.yarep.impl.AbstractNode.PROPERTY_IS_CHECKED_OUT, false); setProperty(org.wyona.yarep.impl.AbstractNode.PROPERTY_CHECKIN_DATE, new Date()); log.warn("TODO: Implementation not finished yet, because no revision returned yet!"); return null; //return revision; } /** * Puts this node into checked-out state. * @throws NodeStateException if node is in checked out state already * @throws RepositoryException repository error */ public void checkout(String userID) throws NodeStateException, RepositoryException { if (isCheckedOut()) { try { throw new NodeStateException("Node " + jcrNode.getPath() + " is already checked out by: " + getCheckoutUserID()); } catch(javax.jcr.RepositoryException e) { log.error(e, e); throw new RepositoryException(e.getMessage(), e); } } setProperty(org.wyona.yarep.impl.AbstractNode.PROPERTY_IS_CHECKED_OUT, true); log.warn("User ID: " + userID); setProperty(org.wyona.yarep.impl.AbstractNode.PROPERTY_CHECKOUT_USER_ID, userID); setProperty(org.wyona.yarep.impl.AbstractNode.PROPERTY_CHECKOUT_DATE, new Date()); } /** * Cancels a checkout, i.e. performs a checkin without creating a new revision. * @throws NodeStateException * @throws NodeStateException if node is not in checked out state * @throws RepositoryException */ public void cancelCheckout() throws NodeStateException, RepositoryException { if (!isCheckedOut()) { try { throw new NodeStateException("Node " + jcrNode.getPath() + " is not checked out."); } catch(javax.jcr.RepositoryException e) { log.error(e, e); throw new RepositoryException(e.getMessage(), e); } } setProperty(org.wyona.yarep.impl.AbstractNode.PROPERTY_IS_CHECKED_OUT, false); setProperty(org.wyona.yarep.impl.AbstractNode.PROPERTY_CHECKIN_DATE, new Date()); } /** * Indicates whether this node is checked out. * @return true if checked out, false otherwise * @throws RepositoryException repository error */ public boolean isCheckedOut() throws RepositoryException { if (!hasProperty(org.wyona.yarep.impl.AbstractNode.PROPERTY_IS_CHECKED_OUT)) { return false; } return getProperty(org.wyona.yarep.impl.AbstractNode.PROPERTY_IS_CHECKED_OUT).getBoolean(); } /** * Gets the userID which was supplied when calling checkout(userID). * @return userID * @throws NodeStateException if node is not checked out. * @throws RepositoryException */ public String getCheckoutUserID() throws NodeStateException, RepositoryException { if (!isCheckedOut()) { try { throw new NodeStateException("Node is not checked out: " + jcrNode.getPath()); } catch(javax.jcr.RepositoryException e) { log.error(e, e); throw new RepositoryException(e.getMessage(), e); } } return getProperty(org.wyona.yarep.impl.AbstractNode.PROPERTY_CHECKOUT_USER_ID).getString(); } /** * Gets the date when this node was checked out. * @return checkout date * @throws NodeStateException if node is not checked out. * @throws RepositoryException */ public Date getCheckoutDate() throws NodeStateException, RepositoryException { log.error("Not implemented yet!"); return null; } /** * Gets the date when this node was checked in. * @return checkin date * @throws NodeStateException if node is not checked in. * @throws RepositoryException */ public Date getCheckinDate() throws NodeStateException, RepositoryException { log.error("Not implemented yet!"); return null; } /** * Gets all revisions of this node. * Oldest revision at the first array position, newest at the last position. * @return array of revisions, or empty array if there are no revisions * @throws RepositoryException */ public Revision[] getRevisions() throws RepositoryException { log.error("Not implemented yet!"); return new Revision[0]; } /** * Gets the revision with the given name. * @param revisionName * @return revision * @throws NoSuchRevisionException if the revision does not exist * @throws RepositoryException */ public Revision getRevision(String revisionName) throws NoSuchRevisionException, RepositoryException { log.error("Not implemented yet!"); return null; } /** * Gets the revision with the given tag. * If multiple revisions have the same tag, the oldest one will be returned. * @param tag * @return revision * @throws NoSuchRevisionException if the revision does not exist * @throws RepositoryException */ public Revision getRevisionByTag(String tag) throws NoSuchRevisionException, RepositoryException { log.error("Not implemented yet!"); return null; } /** * Indicates whether this node has a revision with the given tag. * If multiple revisions have the same tag, the oldest one will be returned. * @param tag * @return true if a revision with the given tag exists, false otherwise * @throws RepositoryException */ public boolean hasRevisionWithTag(String tag) throws RepositoryException { log.error("Not implemented yet!"); return false; } /** * Restores the revision with the given name. * @param revisionName * @throws NoSuchRevisionException if the revision does not exist * @throws RepositoryException */ public void restore(String revisionName) throws NoSuchRevisionException, RepositoryException { log.error("Not implemented yet!"); } /** * Gets the last modified date of this node in ms. * Changing a property should update the last modified date. * @return last modified date in ms * @throws RepositoryException */ public long getLastModified() throws RepositoryException { try { return getJCRResourceNode().getProperty("jcr:lastModified").getDate().getTimeInMillis(); } catch (Exception e) { throw new RepositoryException(e); } } /** * Gets the size of the data content of this node if this node is of type resource. * @return size in bytes * @throws RepositoryException */ public long getSize() throws RepositoryException { log.error("Not implemented yet!"); return -1; } /** * Gets the mimetype of the data content of this node if this node is of type resource. * @return mimetype * @throws RepositoryException */ public String getMimeType() throws RepositoryException { // TODO: check on jcr:content/@jcr:mimeType try { if (jcrNode.getNode("jcr:content").hasProperty("jcr:mimeType")) { return jcrNode.getNode("jcr:content").getProperty("jcr:mimeType").getString(); } } catch (Exception e) { throw new RepositoryException(e); } return null; } /** * Sets the mimetype of the data content of this node if this node is of type resource. * @param mimeType * @throws RepositoryException */ public void setMimeType(String mimeType) throws RepositoryException { // TODO: Use a namespace, e.g. yarep:mimeType // TODO: check on jcr:content/@jcr:mimeType try { if (jcrNode.hasNode("jcr:content")) { //if (type != NodeType.COLLECTION) { jcrNode.getNode("jcr:content").setProperty("jcr:mimeType", mimeType); session.save(); } else { log.warn("Node '" + getPath() + "' seems to be a collection and hence mime type cannot be set!"); } } catch (Exception e) { throw new RepositoryException(e); } } /** * Gets the encoding of the data content of this node if this node is of type resource. * @return encoding * @throws RepositoryException */ public String getEncoding() throws RepositoryException { log.error("Not implemented yet!"); return null; } /** * Sets the encoding of the data content of this node if this node is of type resource. * @param encoding * @throws RepositoryException */ public void setEncoding(String encoding) throws RepositoryException { log.error("Not implemented yet!"); } /** * */ public javax.jcr.Node getJCRNode() { return jcrNode; } /** * */ public javax.jcr.Node getJCRResourceNode() throws Exception { return jcrNode.getNode("jcr:content"); } /** * */ public javax.jcr.Session getJCRSession() { return session; } }